package cn.didi.feidi.javabackend.web.waterdeliverapp.controller.aop;

import cn.didi.feidi.javabackend.mysql.jpa.exception.MyException;
import cn.didi.feidi.javabackend.mysql.jpa.waterDeliver.WaterDeTo;
import cn.didi.feidi.javabackend.server.waterdeliver.annotation.NeedVaild;
import cn.didi.feidi.javabackend.server.waterdeliver.annotation.ParamsVaild;
import cn.didi.feidi.javabackend.server.waterdeliver.dto.UpdateInfo;
import cn.didi.feidi.javabackend.server.waterdeliver.repository.WaterDeToRep;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

/**
 * Created by asus on 2017-07-19.
 */
@Aspect
@Component
public class ControllerInterceptor {

    @Autowired
    WaterDeToRep waterDeToRep;

    /**
     * 匹配waterdeliverapp.controller及其子类中所有方法
     */
    @Pointcut("execution(* cn.didi.feidi.javabackend.web.waterdeliverapp.controller..*.*(..))")
    public void paramsVaild(){

    }

    /**
     * 匹配waterdeliverapp.controller及其子类中所有拥有@TokenVaild的方法
     */
    @Pointcut("execution(* cn.didi.feidi.javabackend.web.waterdeliverapp.controller..*.*(..)) && " +
            "@annotation(cn.didi.feidi.javabackend.server.waterdeliver.annotation.TokenVaild)")
    public void tokenVaild(){

    }

    /**
     * 检验Token是否与数据库中的一致
     * @param joinPoint
     * @throws MyException
     */
    @Before("tokenVaild()")
    public void tokenVaildate(JoinPoint joinPoint) throws MyException{
        Object[] args = joinPoint.getArgs();
        for(Object arg : args){
            if(arg.getClass().equals(UpdateInfo.class)){
                UpdateInfo ui = (UpdateInfo)arg;
                WaterDeTo waterDeTo = waterDeToRep.getByWaterDeliverId(ui.getWaterDeliverId());
                String token = waterDeTo.getToken();
//                System.out.println("original token: "+token);
//                System.out.println("request token: "+ui.getToken());
                if(!token.equals(ui.getToken()))
                    throw new MyException("Token不一致;");
                return ;
            }
        }

    }

    /**
     * 检验传入对象拥有@ParamsVaild的属性是否符合规则
     * @param joinPoint
     * @return
     * @throws Throwable
     */
    @Around("paramsVaild()")
    public Object testVaildParam(ProceedingJoinPoint joinPoint) throws Throwable{
        Object result = null;
        paramsCheck(joinPoint);
        result = ((ProceedingJoinPoint) joinPoint).proceed();
        return result;
    }

    public void paramsCheck(ProceedingJoinPoint joinPoint) throws MyException{
        Method method = null;
        Object[] args = null;
        String methodName = null;
        Object target = null;
        boolean flag = false;

        target = joinPoint.getTarget();
        methodName = joinPoint.getSignature().getName();
        method = getMethodByClassAndName(target.getClass(), methodName);
        Annotation[][] annotations = method.getParameterAnnotations();
        args = joinPoint.getArgs();
        flag = vaildateParams(args, annotations);

    }

    private boolean vaildateParams(Object[] args, Annotation[][] annotations) throws MyException {
        if(annotations != null){
            for(int i = 0; i < annotations.length; i++){
                Annotation[] annotation = annotations[i];
                for(int j = 0; j < annotation.length; j++){
                    if(annotation[j].annotationType().equals(NeedVaild.class)){
                        vaildateParam(args[i]);
                    }
                }
            }
        }
        return true;
    }

    private void vaildateParam(Object arg) throws MyException {
        Field[] fields = arg.getClass().getDeclaredFields();
        for(Field field : fields){
            ParamsVaild pc = field.getAnnotation(ParamsVaild.class);
            if(pc != null){
                vaildateField(field, arg, pc);
            }

        }
    }

    private void vaildateField(Field field, Object arg, ParamsVaild pc) throws MyException {
        field.setAccessible(true);
        if(pc.notNull()){
            try{
                if(field.get(arg) == null || String.valueOf(field.get(arg)).length() == 0)
                    throw new MyException(field.getName()+"不可为空;");
            }catch (IllegalAccessException e) {
                throw new MyException(e.toString());
            }
        }
    }

    private Method getMethodByClassAndName(Class<?> clazz, String methodName){
        Method[] methods = clazz.getDeclaredMethods();
        for(Method method : methods){
            if(method.getName().equals(methodName))
                return method;
        }
        return null;
    }
}
